package com.gitee.wsl.doc.prosemirror.plugin.parser

import com.gitee.wsl.collections.stack.stackOf
import com.gitee.wsl.doc.prosemirror.model.Schema
import com.gitee.wsl.doc.prosemirror.model.attr.Attrs
import com.gitee.wsl.doc.prosemirror.model.mark.Mark
import com.gitee.wsl.doc.prosemirror.model.mark.MarkType
import com.gitee.wsl.doc.prosemirror.model.node.Node
import com.gitee.wsl.doc.prosemirror.model.node.NodeType
import com.gitee.wsl.doc.prosemirror.model.node.ext.maybeMerge

abstract class NodeStack<Token>(
    val schema: Schema
) {
    class InnerNode(
        val type: NodeType,
        val attrs: Attrs? = null,
        val content: MutableList<Node>,
        var marks:  List<Mark>
    )

    val stack = stackOf<InnerNode>()

    init {
      stack.push(InnerNode(
          type = schema.topNodeType,
          attrs = null,
          content = mutableListOf(),
          marks = Mark.none
      ))
    }

    fun top() = this.stack.peekOrNull()

    fun push(elt: Node) = this.top()?.content?.plus(elt)

    fun addText(text: String) {
        if (text.isEmpty()) return

        val top = top()?:return
        val nodes = top.content
        val last = nodes.lastOrNull()
        val node = schema.text(text, top.marks)

        val merged = last?.let { maybeMerge(it, node) }
        if (merged != null) {
            nodes[nodes.size - 1] = merged
        } else {
            nodes.add(node)
        }
    }

    fun openMark(mark: Mark) {
        val top = top()?:return
        top.marks = mark.addToSet(top.marks)
    }

    fun closeMark(mark: MarkType) {
        val top = top()?:return
        top.marks = mark.removeFromSet(top.marks)
    }

    abstract fun parseTokens(toks: List<Token>) /*{
        for (i in toks.indices) {
            val tok = toks[i]
            val handler = tokenHandlers[tok.type] ?: throw IllegalArgumentException("Token type `${tok.type}` not supported by Markdown parser")
            handler(this, tok, toks, i)
        }
    }*/

    fun addNode(type: NodeType, attrs: Attrs?, content: List<Node>? = null): Node? {
        val top = top()?:return null
        val node = type.createAndFill(attrs, content, top.marks) ?: return null
        push(node)
        return node
    }

    fun openNode(type: NodeType, attrs: Attrs?) {
        stack.push(InnerNode(
            type = type,
            attrs = attrs,
            content = mutableListOf(),
            marks = Mark.Companion.none
        ))
    }

    fun closeNode(): Node? {
        val info = stack.popOrNull()?:return null
        return addNode(info.type, info.attrs, info.content)
    }

    fun closeAll():Node?{
        var doc:Node? = null
        while(stack.isNotEmpty()){
            doc = closeNode()
        }
        return doc
    }

}